#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "../include/dictdef.h"
#include "../include/spellingtrie.h"

#include "../include/keymapping.h"
#include "../include/debug.h"

/*
#include <fstream>

using namespace std;
*/

#define DEBUG_KEY

namespace ime_pinyin {

const char KeyMapping::kBellPadKeys[kBellPadKeyNum] = {'2', '3', '4', '5', '6', '7', '8', '9'};

const char KeyMapping::kBellPadMapping[kBellPadKeyNum][kMaxMappingPerKey+1] = {
//			"",			// 0 key
//			"",			// 1 key
			"ABC",		// 2 key
			"DEF",		// 3 key
			"GHI",		// 4 key
			"JKL",		// 5 key
			"MNO",		// 6 key
			"PQRS",		// 7 key
			"TUV",		// 8 key
			"WXYZ"		// 9 key
};

int compare_keymap_item(const void* p1, const void* p2) {
	KeyMappingNode * n1 = (KeyMappingNode *)p1;
	KeyMappingNode * n2 = (KeyMappingNode *)p2;

	if (n1->keymapping_value > n2->keymapping_value)
		return 1;
	if (n1->keymapping_value < n2->keymapping_value)
		return -1;

	if (SpellingTrie::get_instance().is_half_id(n1->spelling_idx) && (!SpellingTrie::get_instance().is_half_id(n2->spelling_idx)))
		return 1;
	if ((!SpellingTrie::get_instance().is_half_id(n1->spelling_idx)) && (SpellingTrie::get_instance().is_half_id(n2->spelling_idx)))
		return -1;

	if (n1->spelling_idx > n2->spelling_idx)
		return 1;
	if (n1->spelling_idx < n2->spelling_idx)
		return -1;

  return 0;
}

	//default one is a bellpad keymapping
KeyMapping::KeyMapping()
{
	//
	table_ = NULL;
	mCurrentStrType = NUM_STR;
	mpInputBuf = new char[kMaxPinyinSize+1+1];
	memset( mpInputBuf, 0, kMaxPinyinSize+1+1 );
}

KeyMapping::~KeyMapping()
{
	//
	if (NULL != table_){
		delete []table_;
		table_ = NULL;
	}
}

// get mapping
const char * KeyMapping::key_to_chars(char key)
{
	if(is_mapped_key(key)) {
		return kBellPadMapping[key-'2'];
	}

	return NULL;
}

//
char KeyMapping::char_to_key(char ch)
{
	char key = 0;

	if (is_mapping_char(ch)) {
		switch(ch){
 		case 'a': case 'b':case 'c':
 		case 'A': case 'B':case 'C':
			key = '2';
			break;
		case 'd':case 'e':case 'f':
		case 'D':case 'E':case 'F':
			key = '3';
			break;
		case 'g':case 'h':case 'i':
		case 'G':case 'H':case 'I':
			key = '4';
			break;
		case 'j':case 'k':case 'l':
		case 'J':case 'K':case 'L':
			key = '5';
			break;
		case 'm':case 'n':case 'o':
		case 'M':case 'N':case 'O':
			key = '6';
			break;
		case 'p':case 'q':case 'r':case 's':
		case 'P':case 'Q':case 'R':case 'S':
			key = '7';
			break;
		case 't':case 'u':case 'v':
		case 'T':case 'U':case 'V':
			key = '8';
			break;
		case 'w':case 'x':case 'y':case 'z':
		case 'W':case 'X':case 'Y':case 'Z':
			key = '9';
			break;
		}
	}

	return key;
}

char * KeyMapping::spl_id_to_keys(uint16 splid)
{
	const char * spl_str = NULL;
	spl_str = SpellingTrie::get_instance().get_spelling_str(splid);

	return spl_to_keys(spl_str);
}

char * KeyMapping::spl_to_keys(const char * spl)
{
	if (NULL == spl)
		return NULL;

	static char keys[kMaxPinyinSize+1+1];

	size_t size = strlen(spl);
	if (size >kMaxPinyinSize)
		size = kMaxPinyinSize;

	for (size_t i =0 ;i<size; i++) {
		keys[i] = char_to_key(spl[i]);
	}
	keys[size] = '\0';

	return keys;
}

//whether the key is mapped
bool KeyMapping::is_mapped_key(char key)
{
	return (key>='2' && key<='9');
}

//whether the char is a mapping character
bool KeyMapping::is_mapping_char(char ch)
{
	return (((ch>='A') && (ch<='Z')) || ((ch>='a') && (ch<='z')));
}

bool KeyMapping::is_valid_to_parse(char ch)
{
	return is_mapped_key(ch) ||is_mapping_char(ch);
}

//TODO : use tree to get higher efficence
bool KeyMapping::construct_key_mapping_table()
{
	bool ret = true;

	if (NULL != table_)
		return ret;

	SpellingTrie& spl_trie_ = SpellingTrie::get_instance();
//	assert(spl_trie);

	table_ = new KeyMappingNode[kMaxTableSize];
	for (int i = 0 ; i < kMaxTableSize; i++) {
		table_[i].keymapping_value = 0;
		table_[i].spelling_idx = 0;
	}
	table_size = 0;
	const char * spl_str = NULL;
	uint32 value;
	for (uint16 splid=1; splid<kMaxTableSize; splid++) {
		if (spl_trie_.is_full_id(splid) ||spl_trie_.is_half_id(splid)) {
			spl_str = spl_trie_.get_spelling_str(splid);
			if (spl_str == NULL)
				continue;
			value = 0;
			for(int i = 0; i< (int)strlen(spl_str);i++) {
				char ch = char_to_key(spl_str[i]);
				if (ch > 0) {
					value = value * 10 + (ch-'0');
				}
			}

			if (value >0) {
				table_[table_size].spelling_idx = splid;
				table_[table_size].keymapping_value = value;
				table_size++;
			}
		}

	}

/////////////////////////////////////////////////////////////////////////
// dump the splid and the pinyin string relation to a file for search during the development
// These code should only use under the debug engine, if you build the engine so for the
// emulator, you should comment them
//////////////////////////////////////////////////////////////////////////

/*
	ofstream outfile( "./splid-pinyin.txt", ofstream::trunc );
	for( int i=0; i<this->table_size; ++i )
	{
		if( 0!=table_[i].keymapping_value )
		{
			const char* tmpStr = SpellingTrie::get_instance().get_spelling_str(table_[i].spelling_idx);
			outfile<<table_[i].spelling_idx<<"     "<<tmpStr<<"\n";
		}
	}
	outfile.close();
	
*/
///////////////
//end dump
//////////////////

	if (table_size >0) {
		qsort(table_, table_size, sizeof(KeyMappingNode), compare_keymap_item);
	}

	return ret;
}

uint32 KeyMapping::str_to_uint( const char* str )
{
	if( NULL==str )
		return 0;

#ifdef DEBUG_KEY
	//printf("======Begin str_to_uint=====\n");
#endif

	mCurrentStrType = NUM_STR;
	char key[kMaxPinyinSize+1+1]={'\0'};
	int keyIndex = 0;
	char ret = 0;
	while( '\0'!=*str )
	{
		if( (ret=char_to_key(*str))>0 )
		{
			key[keyIndex] = ret;
			mCurrentStrType = MIX_STR;
		}
		else
		{
			key[keyIndex] = *str;
		}
		++keyIndex;
		++str;
	}

	key[keyIndex] = '\0';

#ifdef DEBUG_KEY
	//printf("=====End the str_to_uint=====\n");
#endif

	return atoi( key );
}

char KeyMapping::abc_char( const char c )
{
	if( (c>='a')&&(c<='z') )
		return c-'a';
	else if( (c>='A')&&(c<='Z') )
		return c-'A';

	// this is not the mapping key, just return the original value
	return c;
}

bool KeyMapping::py_equal( const char* input, const char* data_from_base )
{
	while( ('\0'!=*input)&&('\0'!=*data_from_base) )
	{
		if( (true==is_mapping_char(*input))&&(abc_char(*input++)==abc_char(*data_from_base++)) )
			continue;
		else
			return false;
	}

	return true;
}

bool KeyMapping::is_splitter( const char c )
{
	// All characters that invalid to parse is treated as splitter
	return (!is_valid_to_parse(c));
//	return ('\''==c);
}

bool KeyMapping::contain_split( const char* input, bool contain_last_char )
{
	int length = strlen(input);
	if( false==contain_last_char )
		length = length - 1;

	for( int i=0; i<length; i++ )
	{
		if( is_splitter(input[i]) )
			return true;
	}

	return false;
}


int KeyMapping::get_mapped_spl_idxs_core( uint32 value, uint16 * spl_idx_buf, int bufSize )
{
	int spl_idx_num_ret = 0;

	for(int i = 0; i<this->table_size;i++){
		if (table_[i].keymapping_value < value)
		{
			continue;
		}
		else if (table_[i].keymapping_value > value)
		{
			break;
		}
		else
		{
			if ( spl_idx_num_ret >= bufSize )
				break;

			if( NUM_STR==mCurrentStrType )
				spl_idx_buf[spl_idx_num_ret++] = table_[i].spelling_idx;
			else
			{
				const char* tmpStr = SpellingTrie::get_instance().get_spelling_str(table_[i].spelling_idx);
				if( true==py_equal( mpInputBuf, tmpStr ) )
					spl_idx_buf[spl_idx_num_ret++] = table_[i].spelling_idx;
			}
		}
	}

	return spl_idx_num_ret;
}

//This is a simple method to get mapped spl_idxs
uint16 * KeyMapping::get_mapped_spl_idxs(const char * keys, int keys_len, uint16 * spl_idx_buf, int max_size, int * spl_idx_num)
{
	//printf( "the param = %s, %d \n", keys, keys_len );
	if (NULL == keys || keys_len ==0 || keys_len >(int)kMaxPinyinSize+1 )
		return NULL;

	if (NULL == spl_idx_buf || NULL == spl_idx_num)
		return NULL;

	if (NULL == table_)
		construct_key_mapping_table();

	strncpy(mpInputBuf, keys, keys_len);
	mpInputBuf[keys_len] = '\0';

	if( true==contain_split(mpInputBuf, false) )
	{
		*spl_idx_num = 0;
		return NULL;
	}
	// Max Spelling length, the last one must be splitter
	else if (keys_len == kMaxPinyinSize+1 && (!is_splitter(mpInputBuf[kMaxPinyinSize]))){
		*spl_idx_num = 0;
		return NULL;
	}

	uint32 value = str_to_uint( mpInputBuf );
#ifdef DEBUG_KEY
	//printf("=====The key = %d char = %s =====\n", value, mpInputBuf );
#endif
	*spl_idx_num = get_mapped_spl_idxs_core( value, spl_idx_buf, max_size );

	return spl_idx_buf;
}

size_t KeyMapping::get_pinyin_from_keys( const char* keys, char* outBuf, int outBufSize, SpellingParser* spl_parser_ )
{
#ifdef DEBUG_KEY
	//printf("*******input key = %S ********\n", keys );
#endif
	if( NULL==spl_parser_ )
		return 0;

	char tmp[kMaxPinyinSize+1+1];
	memset( tmp, 0, kMaxPinyinSize+1+1 );
	int len = strlen( keys );
	if( '\''==keys[len-1] )
		len = len -1;

	strncpy( tmp, keys, len );
#ifdef DEBUG_KEY
	//printf("*******The key = %S ********\n", tmp );
#endif

	uint16 idx_buf[ 50 ];
	memset( idx_buf, 0, 50 );
	int idx_num = 0;

	get_mapped_spl_idxs( tmp, len, idx_buf, 50, &idx_num );
	if( 0==idx_num )
		return 0;

	int outBufIndex = 0;
	for( int i=0; i<idx_num; ++i )
	{
		const char* tmpStr  = SpellingTrie::get_instance().get_spelling_str( idx_buf[i] );
		unsigned char sorce = spl_parser_->get_spl_sorce_by_id( idx_buf[i] );
#ifdef DEBUG_KEY
		printf(" pingyin str = %s; the id = %d, and the sorce is : %d \n", tmpStr, idx_buf[i], sorce);
#endif

		int tmpStr_length = (int)strlen( tmpStr );
		if( (outBufIndex+tmpStr_length+1/*+2*/)<outBufSize-1 )
		{
			if( 0!=sorce )
			{
			    outBufIndex = outBufIndex + tmpStr_length + 1/* + 2*/;
				strcat( outBuf, tmpStr );
//				outBuf[outBufIndex-3] = ':';
//				outBuf[outBufIndex-2] = sorce;
				outBuf[outBufIndex-1] = ';';
			}
		}
		else
			break;
	}
	outBuf[outBufSize-1] = '\0';

	return idx_num;
}

size_t KeyMapping::append_split_info( const char *rawkeys, char* pinyin, int pinyinbuf_length )
{
	int rawkey_length = strlen( rawkeys );
	int pinyin_length = strlen( pinyin );
	char tmpbuf[pinyinbuf_length];
	memset( tmpbuf, 0, pinyinbuf_length );
	int pinyinbuf_index = 0;

	if( (true==contain_split(rawkeys, true))&&(rawkey_length>pinyin_length)&&(rawkey_length<pinyinbuf_length) )
	{
		int i = 0 ;
		for( i=0; i<rawkey_length; ++i )
		{
			if( pinyinbuf_index>pinyin_length )
				break;

			if( false==is_splitter(rawkeys[i]) )
			{
				tmpbuf[i] = pinyin[pinyinbuf_index];
				++pinyinbuf_index;
			}
			else
			{
				tmpbuf[i] = '\'';
			}
		}
		tmpbuf[i] = '\0';
		strcpy( pinyin, tmpbuf );

		return strlen( pinyin );
	}

	return -1;
}
size_t KeyMapping::sort_by_sorce( char* pinyinBuf, size_t buf_length )
{
	//print_cur_time();
	size_t pinyin_count = 0;
	for( size_t i=0; i<buf_length; ++i )
	{
		if( ';'==pinyinBuf[i] )
			++pinyin_count;
	}

	//construct the info list
//	size_t max_pinyin_str_length = 6;
	struct sort_info
	{
		size_t begin_of_pinyin_index;
		char   score_of_pinyin;
	};
	sort_info info[pinyin_count];
	memset( info, 0, sizeof(sort_info)*pinyin_count );
	info[0].begin_of_pinyin_index = 0;
	size_t info_index = 0;
	for( size_t i=0; i<buf_length; ++i )
	{
		if( ';'==pinyinBuf[i] )
			info[info_index].score_of_pinyin = pinyinBuf[i-1];
		if( i-1>0&&';'==pinyinBuf[i-1] )
			info[++info_index].begin_of_pinyin_index = i;
	}

	for( size_t i=0; i<pinyin_count; ++i )
	{
		for( size_t j=i+1; j<pinyin_count; ++j )
		{
			if( info[j].score_of_pinyin<info[i].score_of_pinyin )
			{
				sort_info tmp;
				tmp.begin_of_pinyin_index = info[i].begin_of_pinyin_index;
				tmp.score_of_pinyin = info[i].score_of_pinyin;

				info[i].begin_of_pinyin_index = info[j].begin_of_pinyin_index;
				info[i].score_of_pinyin = info[j].score_of_pinyin;

				info[j].begin_of_pinyin_index = tmp.begin_of_pinyin_index;
				info[j].score_of_pinyin = tmp.score_of_pinyin;
			}
		}
	}

	char tmpBuf[buf_length];
	size_t index = 0;
	memset( tmpBuf, 0, buf_length );
	for( size_t i=0; i<pinyin_count; ++i )
	{
		for( int j=info[i].begin_of_pinyin_index; ':'!=pinyinBuf[j]; ++j )
		{
			tmpBuf[index++] = pinyinBuf[j];
		}
		tmpBuf[index++] = ';';
	}
	tmpBuf[index] = '\0';

	memset( pinyinBuf, 0, buf_length );
	strcpy( pinyinBuf, tmpBuf );

	//print_cur_time();

	return pinyin_count;

}

}
